#include <math.h>
#include <float.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <fstream>
#include "Raytracer.h"
#include <list>
#include <iomanip>
#include <process.h>
#include <stdio.h>
#include "Logger.h"
#include <sstream> 
#include "util.h"


using namespace std;
using namespace loggingService;
using namespace util;

//
// inspired by Luke Hoban's Blog
// http://blogs.msdn.com/b/lukeh/archive/2007/04/03/a-ray-tracer-in-c-3-0.aspx
//

namespace raytracer{

	const double PI = 3.14159265;	

	//
	// Implementations of Vector class
	//

	Vector::Vector(){
	}

	Vector::Vector(double x, double y, double z) 
	{ 
		X = x; 
		Y = y; 
		Z = z; 
	}

	Vector::Vector(string &s)
	{
		StringTokenizer tokenizer = StringTokenizer(s,",");
		this->X = atof(tokenizer.getNextElement().c_str());
		this->Y = atof(tokenizer.getNextElement().c_str());
		this->Z = atof(tokenizer.getNextElement().c_str());
	}

	Vector Vector::times(double n, Vector &v) 
	{
		return Vector(v.X * n, v.Y * n, v.Z * n);
	}

	Vector Vector::minus(Vector &v1, Vector &v2) 
	{
		return Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
	}

	Vector Vector::plus(Vector &v1, Vector &v2) 
	{
		return Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
	}

	double Vector::dot(Vector &v1, Vector &v2) 
	{
		return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z);
	}

	double Vector::magnitude(Vector &v) 
	{ 
		return sqrt(dot(v, v)); 
	}

	Vector Vector::normalize(Vector &v) 
	{
		double mag = magnitude(v);
		double div = mag == 0 ? DBL_MAX : 1 / mag;
		return times(div, v);
	}

	Vector Vector::cross(Vector &v1, Vector &v2) 
	{
		return Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)),
					 ((v1.Z * v2.X) - (v1.X * v2.Z)),
					 ((v1.X * v2.Y) - (v1.Y * v2.X)));
	}

	bool Vector::equals(Vector &v1, Vector &v2) {
		return (v1.X == v2.X) && 
			(v1.Y == v2.Y) && 
			(v1.Z == v2.Z);
	}

	double Vector::angleBetween(Vector &v1, Vector &v2){
		return acos(Vector::dot(v1,v2) / (Vector::magnitude(v1) * Vector::magnitude(v2))) * 180.0 / PI;
	}

	//
	// Implementations of Ray class
	//
	Ray::Ray(){
		start=Vector(0,0,0);
		direction=Vector(0,0,0);
	}

	//
	// Implementations of Intersect class
	//
	Intersect::Intersect(){
		Ray = Ray::Ray();
		object=NULL;
		distance=0;
	}

	//
	// Implementations of Object
	//
	Object::Object(){
		//surface = raytracer::Surface();
	}

	Object::Object(const Object& object){
		this->surface = object.surface;
	}

	bool Object::intersect(Ray &ray, Intersect &intersect){
		return false;
	}

	Vector Object::normal(Vector &pos){
		return Vector(0,0,0);
	}


	//
	// Implementations of Sphere class
	//

	Sphere::Sphere(){
		position=Vector(0,0,0);
		radius=0;
	}

	Sphere::Sphere(const Sphere& sphere){
		this->surface = sphere.surface;
		this->radius = sphere.radius;
		this->position = sphere.position;
	}

	bool Sphere::intersect(Ray &ray,Intersect &intersect){
		
		Vector eo = Vector::minus(position, ray.start);
        double v = Vector::dot(eo, ray.direction);
        double dist;
        if (v < 0)
        {
            dist = 0;
        }
        else
        {
            double disc = radius * radius - (Vector::dot(eo, eo) - v*v);
            dist = disc < 0 ? 0 : v - sqrt(disc);
        }
        
		if (dist == 0)
		{
			return false;
		}		
		intersect.object = this;
		intersect.Ray = ray;
		intersect.distance = dist;
		return true;
	}

	Vector Sphere::normal(Vector &position){
		return Vector::normalize(Vector::minus(position, Sphere::position));
	}

	//
	// Implementations of Plane class
	//

	Vector Plane::normal(Vector &pos){
		return myNormal;
	}

	Plane::Plane(){
		myNormal = Vector(0,0,0);
		offset = 0;
	}

	Plane::Plane(const Plane& plane){
		this->surface = plane.surface;
		this->myNormal = plane.myNormal;
		this->offset = plane.offset;
	}

	bool Plane::intersect(Ray &ray, Intersect &intersect){
		
		double denom = Vector::dot(myNormal, ray.direction);

		if (denom > 0) return false;		

		intersect.object = this;
		intersect.Ray = ray;
		intersect.distance = (Vector::dot(myNormal, ray.start) + offset) / (-denom);
		return true;
	}		

	//
	// Implementations of Color class
	//

	double colorvalue(double d)
	{
        return (d > 1 ? 1 : (d < 0 ? 0 : d));
	}

	Color::Color()
	{
		R=0;
		G=0;
		B=0;
	}

	Color::Color(double r, double g, double b){
		R=r;
		G=g;
		B=b;
	}

	Color::Color(string &s){
		StringTokenizer tokenizer = StringTokenizer(s,",");
		R = atof(tokenizer.getNextElement().c_str());
		G = atof(tokenizer.getNextElement().c_str());
		B = atof(tokenizer.getNextElement().c_str());
	}

	Color Color::times(double n, Color &c)
	{
		return Color(colorvalue(c.R*n),colorvalue(c.G*n),colorvalue(c.B*n));
	}

	Color Color::times(Color &c1, Color &c2)
	{
		return Color(colorvalue(c1.R*c2.R),colorvalue(c1.G*c2.G),colorvalue(c1.B*c2.B));
	}

	Color Color::plus(Color &c1, Color &c2)
	{
		return Color(colorvalue(c1.R+c2.R),colorvalue(c1.G+c2.G),colorvalue(c1.B+c2.B));
	}

	Color Color::minus(Color &c1, Color &c2)
	{
		return Color(colorvalue(c1.R-c2.R),colorvalue(c1.G-c2.G),colorvalue(c1.B-c2.B));
	}

	bool Color::equals(Color &c1, Color &c2)
	{
		return (c1.R == c2.R) && 
			(c1.G == c2.G) && 
			(c1.B == c2.B);
	}
	
	//
	// Implementations Surface class
	//
	Surface::Surface(){
		roughness=0;
	}

	Color Surface::diffuse(Vector &v){
		return Color(1, 1, 1);
	}

	Color Surface::specular(Vector &v){
		return Color(0.5, 0.5, 0.5);
	}

	double Surface::reflect(Vector &v){
		return 0;
	}

	//
	// Implementations of CheckerBoard_Surface class
	//

	CheckerBoard_Surface::CheckerBoard_Surface(){
		roughness=150;
	}

	Color CheckerBoard_Surface::diffuse(Vector &v){
		return ((int)(floor(v.Z) + floor(v.X)) % 2 != 0 ? Color(1, 1, 1) : Color(0, 0, 0));
	}

	Color CheckerBoard_Surface::specular(Vector &v){
		return Color(1, 1, 1);
	}

	double CheckerBoard_Surface::reflect(Vector &v){
		return ((int)(floor(v.Z) + floor(v.X)) % 2 != 0) ? 0.05 : 0.1;
	}

	//
	// Implementations of Shiny_Surface class
	//

	Shiny_Surface::Shiny_Surface(){
		roughness=50;
	}

	Color Shiny_Surface::diffuse(Vector &v){
		return Color(1, 1, 1);
	}

	Color Shiny_Surface::specular(Vector &v){
		return Color(0.5, 0.5, 0.5);
	}

	double Shiny_Surface::reflect(Vector &v){
		return .7;
	}

	//
	// Implementations of Camera class
	//

	Camera::Camera()
	{
		
	}

	Camera::Camera(Vector position, Vector lookAt)
	{
		this->position = position;
		forward = Vector::normalize(Vector::minus(lookAt, position));
		Vector down = Vector(0, -1, 0);
		right = Vector::times(1.5, Vector::normalize(Vector::cross(forward, down)));
		up = Vector::times(1.5, Vector::normalize(Vector::cross(forward, right)));
	}

	//
	// Implementations of Scene class
	//

	Scene::Scene(){
		things = list<Object*>();
		lights = list<Light*>();
	}

	list<raytracer::Intersect> Scene::intersect(Ray ray){
		
		list<raytracer::Intersect> intersections = list<raytracer::Intersect>();
		list<raytracer::Object*>::iterator iterObjects;
		for(iterObjects = things.begin(); iterObjects != things.end(); ++iterObjects)
		{	
			raytracer::Intersect inter = raytracer::Intersect();
			if(iterObjects._Ptr->_Myval->intersect(ray,inter)){
				intersections.insert(intersections.end(),inter);
			}
		}
		return intersections;
	}

	//
	// Implementations of Raytracer class
	//

	Raytracer::Raytracer(Bitmap &bitmap, int maxdepth,int totalwidth, int totalheight,int left,int top){
		Maxdepth = maxdepth;
		this->bitmap = bitmap;
		this->totalwidth = totalwidth;
		this->totalheight = totalheight;
		this->left = left;
		this->top = top;
	};

	bool compare_intersections (Intersect &first, Intersect &second)
	{
		if (first.distance < second.distance){
			return true;
		}
		return false;
	}

	list<Intersect> Raytracer::Intersections(Ray &ray, Scene &scene)
	{
		list<raytracer::Intersect> intersections = list<raytracer::Intersect>();
		list<raytracer::Object*>::iterator iterObjects;
		for(iterObjects = scene.things.begin(); iterObjects != scene.things.end(); ++iterObjects)
		{	
			raytracer::Intersect inter = raytracer::Intersect();
			if(iterObjects._Ptr->_Myval->intersect(ray,inter)){
				intersections.insert(intersections.end(),inter);
			}
		}

		intersections.sort(compare_intersections);
		return intersections;
	}

	double Raytracer::testRay(Ray &ray, Scene &scene){
		list<raytracer::Intersect> isects = Intersections(ray, scene);
		if(isects.size() == 0){
			return 0;
		}
		Intersect isect = isects.front();
		return isect.distance;
	}

	Color Raytracer::traceRay(Ray &ray, Scene &scene, int depth)
	{
        list<raytracer::Intersect> isects = Intersections(ray, scene);
		if(isects.size() == 0){
			return Color(0,0,0);
		}
		Intersect isect = isects.front();
        return shade(isect, scene, depth);
	}

	Color Raytracer::getNaturalColor(Object &thing, Vector &pos, Vector &norm, Vector &rd, Scene &scene)
	{
		Color ret = Color(0, 0, 0);
        
		list<raytracer::Light*>::iterator iterLights;
		for(iterLights = scene.lights.begin(); iterLights != scene.lights.end(); ++iterLights)		
		{
			Vector ldis = Vector::minus(iterLights._Ptr->_Myval->position, pos);
			Vector livec = Vector::normalize(ldis);
			Ray ray = Ray();
			ray.start = pos;
			ray.direction = livec;
			double neatIsect = testRay(ray, scene);
			bool isInShadow = !((neatIsect > Vector::magnitude(ldis)) || (neatIsect == 0));
			    if (!isInShadow)
			    {
				double illum = Vector::dot(livec, norm);
						Color lcolor;
						if(illum > 0){
							lcolor=Color::times(illum, iterLights._Ptr->_Myval->color) ;
						}else{
							lcolor=Color(0, 0, 0);
						}
						double specular = Vector::dot(livec, Vector::normalize(rd));
						Color scolor;
						if(specular > 0 ){
							scolor = Color::times(pow(specular, thing.surface->roughness), iterLights._Ptr->_Myval->color);
						}else{
							scolor = Color(0, 0, 0);
						}
				ret = Color::plus(ret, Color::plus(Color::times(thing.surface->diffuse(pos), lcolor),
								    Color::times(thing.surface->specular(pos), scolor)));
			    }
		}
        return ret;
	}

	Color Raytracer::getReflectionColor(Object &thing, Vector &pos, Vector &norm, Vector &rd, Scene &scene, int depth)
	{
		Ray ray = Ray();
		ray.start = pos;
		ray.direction=rd;		
        return Color::times(thing.surface->reflect(pos), traceRay(ray, scene, depth + 1));
	}

	Color Raytracer::shade(Intersect &isect, Scene &scene, int depth){
		Vector d = isect.Ray.direction;
		Vector pos = Vector::plus(Vector::times(isect.distance, isect.Ray.direction), isect.Ray.start);
		Vector normal = isect.object->normal(pos);
        Vector reflectDir = Vector::minus(d, Vector::times(2 * Vector::dot(normal, d), normal));
        Color ret = Color(0,0,0);
		ret = Color::plus(ret, getNaturalColor(*(isect.object), pos, normal, reflectDir, scene));
        if (depth >= Maxdepth)
        {
            return Color::plus(ret, Color(0.5, 0.5, 0.5));
        }
		return Color::plus(ret, getReflectionColor(*(isect.object), Vector::plus(pos, Vector::times(.001, reflectDir)), normal, reflectDir, scene, depth));
	}

	double Raytracer::recenterX(double x){
		return (x - (bitmap.getWidth() / 2.0)) / (2.0 * bitmap.getWidth());
	}

	double Raytracer::recenterY(double y){
		return (y - (bitmap.getHeight() / 2.0)) / (2.0 * bitmap.getHeight());
	}

	Vector Raytracer::getPoint(double x, double y, Camera &camera)
	{
		return Vector::normalize(Vector::plus(camera.forward, Vector::plus(Vector::times(recenterX(x), camera.right),
                                                                    Vector::times(recenterY(y), camera.up))));
	}

	int progresseven=0;
	int progressodd=0;
	HANDLE h1;
	HANDLE h2;

	DWORD WINAPI renderWorker(LPVOID parameter)
	{			
		ThreadData* data = (ThreadData*)parameter;
		stringstream ss;
		ss << "RenderWorker " << (data->even == true ? "'even'" : "'odd'") << " starts";
		Logger::getLogger()->log(info,ss.str());

		float ratioX = (float)data->bitmap->getWidth() / data->totalwidth;
		float ratioY = (float)data->bitmap->getHeight() / data->totalheight;		
		
		for (int y = 0; y < data->bitmap->getHeight(); y++)
		{			
				if(data->even && y%2==0 || !data->even && y%2==1){				
					for (int x = 0; x < data->bitmap->getWidth(); x++)
					{
						Ray ray = Ray();
						ray.start = data->scene->camera.position;
						ray.direction = data->raytraycer->getPoint(x * ratioX + data->left * ratioX,y * ratioY + data->top * ratioY,data->scene->camera);
						Color color = data->raytraycer->traceRay(ray, *data->scene, 0);
						data->bitmap->drawPoint(x, y, color);
					}
					if(data->even){
						progresseven++;						
						float progress = (float)(progresseven + progressodd) / data->bitmap->getHeight();
						cout << "[";
						for(int i=0;i<=(int)(40*progress);i++)
						{
							cout << "#";
						}
						for(int i=(40*progress); i<40;i++){
							cout << " ";
						}
						cout << "] " << progress * 100 << "%     \r";
					}else{
						progressodd++;
					}
				}
		}

		if(data->even){
			cout << "\r\n";			
		}else{
			DWORD wait = WaitForSingleObject(h1, INFINITE);
		}
		Logger::getLogger()->log(info,"RenderWorker " + (data->even == true ? (string)"'even'" : (string)"'odd'") + " ends");
		return 1;
	}
	
	void Raytracer::render(Scene &scene){

		this->scene = scene;
		stringstream ss;
		ss << "Start rendering a bitmap with resolution " << bitmap.getWidth() << "*" << bitmap.getHeight() << " pixels with " << 
			(bitmap.getWidth() * bitmap.getHeight()) << " rays";		
		Logger::getLogger()->log(info,ss.str());

		ThreadData* threadData = new ThreadData;
		threadData->bitmap=&bitmap;
		threadData->scene=&scene;
		threadData->raytraycer=this;
		threadData->even=true;
		threadData->totalwidth = this->totalwidth;
		threadData->totalheight = this->totalheight;
		threadData->left = this->left;
		threadData->top = this->top;

		ThreadData* threadData2 = new ThreadData;
		threadData2->bitmap=&bitmap;
		threadData2->scene=&scene;
		threadData2->raytraycer=this;
		threadData2->even=false;
		threadData2->totalwidth = this->totalwidth;
		threadData2->totalheight = this->totalheight;
		threadData2->left = this->left;
		threadData2->top = this->top;
		
		h1 = CreateThread(0, 0, renderWorker, (void*)threadData, 0, NULL);
		h2 = CreateThread(0, 0, renderWorker, (void*)threadData2, 0, NULL);

		DWORD wait1 = WaitForSingleObject(h1, INFINITE);
		DWORD wait2 = WaitForSingleObject(h2, INFINITE);

		delete threadData;
		delete threadData2;

		Logger::getLogger()->log(info,"Rendering finished");
	}

	//
	// Implementations of Bitmap class
	//

	Bitmap::Bitmap(){
		this->buffer = NULL;
		this->Width=0;
		this->Height=0;
	}

	Bitmap::Bitmap(const Bitmap& bitmap){
		this->buffer = bitmap.buffer;
		this->Height = bitmap.Height;
		this->Width = bitmap.Width;
	}

	Bitmap::Bitmap(int width, int height)
	{
		buffer = (unsigned char*)malloc(width*height*3);
		for(int i=0;i<width*height*3;i++)
		{
			buffer[i]=0;
		}
		Width=width;
		Height=height;
	}

	Bitmap::~Bitmap(void)
	{		
		free(buffer);
	}

	void Bitmap::save(string filename)
	{	
		Logger::getLogger()->log(info,"Saving bitmap to '" + filename + "'");
		BITMAPFILEHEADER fileheader;
		BITMAPINFOHEADER infoheader;

		fileheader.bfType = 'MB';
		fileheader.bfSize = sizeof(fileheader.bfOffBits) + sizeof(RGBTRIPLE); 
		fileheader.bfReserved1 = 0;
		fileheader.bfReserved2 = 0;
		fileheader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
 
		infoheader.biSize = sizeof(infoheader);
		infoheader.biWidth = Width;
		infoheader.biHeight = Height;
		infoheader.biPlanes = 1;
		infoheader.biBitCount = 24;
		infoheader.biCompression = 0;
		infoheader.biSizeImage = Width*Height*3;
		infoheader.biXPelsPerMeter = 0;
		infoheader.biYPelsPerMeter = 0;
		infoheader.biClrUsed = 0;
		infoheader.biClrImportant = 0;

		ofstream bmpfile (filename,ios::out | ios::binary);
 
		bmpfile.write ((char*) &fileheader,sizeof(fileheader)); 
		bmpfile.write ((char*) &infoheader,sizeof(infoheader));
		bmpfile.write((char*) buffer,Width*Height*3);
		bmpfile.close();
		Logger::getLogger()->log(info,"Bitmap written");
	}
	
	void Bitmap::load(string filename)
	{	
		Logger::getLogger()->log(info,"Loading bitmap '" + filename + "'");

		char* datBuff[2] = {nullptr, nullptr};
		
		BITMAPFILEHEADER* bmpHeader = nullptr;
		BITMAPINFOHEADER* bmpInfo   = nullptr;

		std::ifstream file(filename, std::ios::binary);

		datBuff[0] = new char[sizeof(BITMAPFILEHEADER)];
		datBuff[1] = new char[sizeof(BITMAPINFOHEADER)];

		file.read((char*)datBuff[0], sizeof(BITMAPFILEHEADER));
		file.read((char*)datBuff[1], sizeof(BITMAPINFOHEADER));

		bmpHeader = (BITMAPFILEHEADER*) datBuff[0];
		bmpInfo   = (BITMAPINFOHEADER*) datBuff[1];

		buffer = new unsigned char[bmpInfo->biSizeImage];

		file.seekg(bmpHeader->bfOffBits);
		file.read((char*)buffer, bmpInfo->biSizeImage);

		file.close();
		
		Width = bmpInfo->biWidth;
		Height = bmpInfo->biHeight;

		free(datBuff[0]);
		free(datBuff[1]);			

		Logger::getLogger()->log(info,"Bitmap loaded");
	}

	void Bitmap::drawPoint(int x, int y, Color &c)
	{
		unsigned char r = c.R * 255;
		unsigned char g = c.G * 255;
		unsigned char b = c.B * 255;

		buffer[x * 3 + y * Width * 3] = b;
		buffer[x * 3 + y * Width * 3 + 1] = g;
		buffer[x * 3 + y * Width * 3 + 2] = r;
	}

	Color Bitmap::getColorOfPoint(int x, int y){
		return Color((double)buffer[x * 3 + y * Width * 3 + 2] / 255,
					 (double)buffer[x * 3 + y * Width * 3 + 1] / 255,
					 (double)buffer[x * 3 + y * Width * 3] / 255);
	}

	void Bitmap::drawLine(int x0, int y0, int x1, int y1, Color &c)
	{
		int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
		int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1; 
		int err = dx+dy, e2;
 
		for(;;)
		{ 
			drawPoint(x0,y0,c);
			if (x0==x1 && y0==y1) break;
			e2 = 2*err;
			if (e2 >= dy) { err += dy; x0 += sx; }
			if (e2 <= dx) { err += dx; y0 += sy; }
		}
	}
	
	void Bitmap::drawRectangle(int x0, int y0, int x1, int y1, Color &c)
	{
		drawLine(x0,y0,x1,y0,c);
		drawLine(x1,y0,x1,y1,c);
		drawLine(x1,y1,x0,y1,c);
		drawLine(x0,y1,x0,y0,c);
	}

	void Bitmap::drawCircle(int x0, int y0, int radius, Color &c)
	{
		 int f = 1 - radius;
			int ddF_x = 0;
			int ddF_y = -2 * radius;
			int x = 0;
			int y = radius;
 
			drawPoint(x0, y0 + radius,c);
			drawPoint(x0, y0 - radius,c);
			drawPoint(x0 + radius, y0,c);
			drawPoint(x0 - radius, y0,c);
 
			while(x < y) 
			{
			  if(f >= 0) 
			  {
				y--;
				ddF_y += 2;
				f += ddF_y;
			  }
			  x++;
			  ddF_x += 2;
			  f += ddF_x + 1;
 
			  drawPoint(x0 + x, y0 + y,c);
			  drawPoint(x0 - x, y0 + y,c);
			  drawPoint(x0 + x, y0 - y,c);
			  drawPoint(x0 - x, y0 - y,c);
			  drawPoint(x0 + y, y0 + x,c);
			  drawPoint(x0 - y, y0 + x,c);
			  drawPoint(x0 + y, y0 - x,c);
			  drawPoint(x0 - y, y0 - x,c);
			}
	}

	void Bitmap::superSampleBitmap(Bitmap& bitmap){
		Logger::getLogger()->log(info,"Start supersampling");
		for(int x=1;x<bitmap.Width-1;x++){
			for(int y=1;y<bitmap.Height-1;y++){				
				Color color = Color::times(0.6,bitmap.getColorOfPoint(x,y));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x-1,y-1)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x,y-1)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x+1,y-1)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x-1,y)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x+1,y)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x-1,y+1)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x,y+1)));
				color = Color::plus(color, Color::times(0.05,bitmap.getColorOfPoint(x+1,y+1)));
				drawPoint(x,y,color);
			}
		}
		Logger::getLogger()->log(info,"Finished supersampling");
	}

	int Bitmap::getHeight()
	{
		return Height;
	}

	int Bitmap::getWidth()
	{
		return Width;
	}

	//
	// Implementations of operators
	//

	Vector operator+ (Vector &v1,Vector &v2)
	{
		return Vector::plus(v1,v2);
	}

	Vector operator- (Vector &v1,Vector &v2)
	{
		return Vector::minus(v1,v2);
	}

	Vector operator* (double n,Vector &v2)
	{
		return Vector::times(n,v2);
	}

	double operator* (Vector &v1,Vector &v2)
	{
		return Vector::dot(v1,v2);
	}

	Vector operator^ (Vector &v1,Vector &v2)
	{
		return Vector::cross(v1,v2);
	}

	bool operator== (Vector &v1,Vector &v2)
	{
		return Vector::equals(v1,v2);
	}

	ostream & operator<< (ostream &os, const Vector &v)
	{
		os << "(" << v.X << "," << v.Y << "," << v.Z <<")";
		return os;
	}
		
	ostream & operator<< (ostream &os, const Ray &ray)
	{
		os << "(start:" << ray.start << "direction:" << ray.direction <<")";
		return os;
	}

	ostream & operator<< (ostream &os, const Color &c)
	{
		os << "(" << c.R << "," << c.G << "," << c.B <<")";
		return os;
	}

	Color operator+ (Color &c1,Color &c2)
	{
		return Color::plus(c1,c2);
	}

	Color operator- (Color &c1,Color &c2)
	{
		return Color::minus(c1,c2);
	}

	Color operator* (unsigned char n,Color &c)
	{
		return Color::times(n,c);
	}

	Color operator* (Color &c1,Color &c2)
	{
		return Color::times(c1,c2);
	}

	bool operator== (Color &c1,Color &c2)
	{
		return Color::equals(c1,c2);
	}
}